// 版权所有2011 Go作者。版权所有。
// 此源代码的使用受BSD样式的约束
// 可以在许可证文件中找到的许可证。

// 包ecdsa实现椭圆曲线数字签名算法，如下所示
// 定义见FIPS 186-3。
// None
// 此实现从AES-CTR CSPRNG中获取nonce，该CSPRNG由以下键控：
// None
// SHA2-512（priv.D | |熵| |散列）[:32]
// None
// CSPRNG密钥与随机oracle不可区分，如中所示
// [Coron]，AES-CTR流与随机oracle是不可区分的
// 在标准加密假设下（示例见[Larsson]）。
// None
// 参考资料：
// [科隆]
// https:
// [拉尔森]
// https:
package ecdsa

// 其他参考资料：
// [NSA]：FIPS 186-3套件B实施者指南
// https:
// [SECG]：SECG，SEC1
// http:

import (
	"crypto"
	"crypto/aes"
	"crypto/cipher"
	"crypto/elliptic"
	"crypto/internal/randutil"
	"crypto/sha512"
	"errors"
	"io"
	"math/big"

	"golang.org/x/crypto/cryptobyte"
	"golang.org/x/crypto/cryptobyte/asn1"
)

// 可逆函数实现快速反模曲线.Params（）.N
type invertible interface {
	// 反转返回GF（P）中k的倒数
	Inverse(k *big.Int) *big.Int
}

// combinedMult实现快速乘法S1*g+S2*p（g-生成器，p-任意点）
type combinedMult interface {
	CombinedMult(bigX, bigY *big.Int, baseScalar, scalar []byte) (x, y *big.Int)
}

const (
	aesIV = "IV for ECDSA CTR"
)

// 公钥表示ECDSA公钥。
type PublicKey struct {
	elliptic.Curve
	X, Y *big.Int
}

// 在公钥上实现的任何方法可能也需要在公钥上实现
// PrivateKey，后者嵌入前者并公开其方法。

// Equal报告pub和x是否具有相同的值。
// None
// 只有当两个关键点具有相同的曲线值时，才会认为它们具有相同的值。
// 请注意，例如椭圆.P256（）和椭圆.P256（）.Params（）是不同的
// 值，因为后者是一个通用的非恒定时间实现。
func (pub *PublicKey) Equal(x crypto.PublicKey) bool {
	xx, ok := x.(*PublicKey)
	if !ok {
		return false
	}
	return pub.X.Cmp(xx.X) == 0 && pub.Y.Cmp(xx.Y) == 0 &&
		// 标准库曲线的实现是单例的，所以这个检查
		// 我会为那些人工作的。其他曲线可能是等效的，即使不是
		// 单身人士，但没有确定的方法来检查，而且
		// 宁可在安全方面犯错。
		pub.Curve == xx.Curve
}

// PrivateKey表示ECDSA私钥。
type PrivateKey struct {
	PublicKey
	D *big.Int
}

// Public返回与priv相对应的公钥。
func (priv *PrivateKey) Public() crypto.PublicKey {
	return &priv.PublicKey
}

// Equal报告priv和x是否具有相同的值。
// None
// 有关如何比较曲线的详细信息，请参见PublicKey.Equal。
func (priv *PrivateKey) Equal(x crypto.PrivateKey) bool {
	xx, ok := x.(*PrivateKey)
	if !ok {
		return false
	}
	return priv.PublicKey.Equal(&xx.PublicKey) && priv.D.Cmp(xx.D) == 0
}

// 与priv签署摘要，阅读兰德公司的随机性。opts论点
// 当前未使用，但根据crypto.Signer接口，
// 应该是用于摘要消息的哈希函数。
// None
// 此方法实现crypto.Signer，这是一个支持密钥的接口
// 其中私有部分保存在例如硬件模块中。常见的
// 用户应直接使用此软件包中的Sign函数。
func (priv *PrivateKey) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) {
	r, s, err := Sign(rand, priv, digest)
	if err != nil {
		return nil, err
	}

	var b cryptobyte.Builder
	b.AddASN1(asn1.SEQUENCE, func(b *cryptobyte.Builder) {
		b.AddASN1BigInt(r)
		b.AddASN1BigInt(s)
	})
	return b.Bytes()
}

var one = new(big.Int).SetInt64(1)

// randFieldElement返回给定字段基础字段的随机元素
// 使用[NSA]A.2.1中给出的程序绘制曲线。
func randFieldElement(c elliptic.Curve, rand io.Reader) (k *big.Int, err error) {
	params := c.Params()
	b := make([]byte, params.BitSize/8+8)
	_, err = io.ReadFull(rand, b)
	if err != nil {
		return
	}

	k = new(big.Int).SetBytes(b)
	n := new(big.Int).Sub(params.N, one)
	k.Mod(k, n)
	k.Add(k, one)
	return
}

// GenerateKey生成公钥和私钥对。
func GenerateKey(c elliptic.Curve, rand io.Reader) (*PrivateKey, error) {
	k, err := randFieldElement(c, rand)
	if err != nil {
		return nil, err
	}

	priv := new(PrivateKey)
	priv.PublicKey.Curve = c
	priv.D = k
	priv.PublicKey.X, priv.PublicKey.Y = c.ScalarBaseMult(k.Bytes())
	return priv, nil
}

// hashToInt将哈希值转换为整数。有一些分歧
// 关于如何做到这一点。[国家安全局]认为这是显而易见的
// 方式，但[SECG]将哈希截断为曲线顺序的位长度
// 第一我们遵循[SECG]，因为OpenSSL就是这样做的。另外,，
// 如果散列太大，OpenSSL会将多余的位从数字右移
// 我们也反映了这一点。
func hashToInt(hash []byte, c elliptic.Curve) *big.Int {
	orderBits := c.Params().N.BitLen()
	orderBytes := (orderBits + 7) / 8
	if len(hash) > orderBytes {
		hash = hash[:orderBytes]
	}

	ret := new(big.Int).SetBytes(hash)
	excess := len(hash)*8 - orderBits
	if excess > 0 {
		ret.Rsh(ret, uint(excess))
	}
	return ret
}

// 费马逆利用费马方法计算GF（P）中k的逆。
// 这比欧几里得方法（已实现）具有更好的恒定时间特性
// 在math/big.Int.ModInverse中）虽然math/big本身并不严格
// 恒定的时间，所以它不是完美的。
func fermatInverse(k, N *big.Int) *big.Int {
	two := big.NewInt(2)
	nMinus2 := new(big.Int).Sub(N, two)
	return new(big.Int).Exp(k, nMinus2, N)
}

var errZeroParam = errors.New("zero parameter")

// Sign对散列进行签名（这应该是对较大消息进行散列的结果）
// 使用私钥priv。如果散列长度大于
// 私钥的曲线顺序，哈希将被截断为该长度。信息技术
// 将签名作为一对整数返回。私钥的安全性
// 取决于兰德的熵。
func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err error) {
	randutil.MaybeReadByte(rand)

	// 从rand中获取最小（log2（q）/2256）位的熵。
	entropylen := (priv.Curve.Params().BitSize + 7) / 16
	if entropylen > 32 {
		entropylen = 32
	}
	entropy := make([]byte, entropylen)
	_, err = io.ReadFull(rand, entropy)
	if err != nil {
		return
	}

	// 初始化SHA-512哈希上下文；消化
	md := sha512.New()
	md.Write(priv.D.Bytes()) // 私钥，
	md.Write(entropy)        // 熵，
	md.Write(hash)           // 以及输入散列；
	key := md.Sum(nil)[:32]  // 并计算出MD-256（SHA-512），
	// 这是一个不可区分的MAC。

	// 创建一个AES-CTR实例用作CSPRNG。
	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, nil, err
	}

	// 创建一个CSPRNG，该CSPRNG使用
	// AES-CTR实例的输出。
	csprng := cipher.StreamReader{
		R: zeroReader,
		S: cipher.NewCTR(block, []byte(aesIV)),
	}

	// 见[NSA]3.4.1
	c := priv.PublicKey.Curve
	return sign(priv, &csprng, c, hash)
}

func signGeneric(priv *PrivateKey, csprng *cipher.StreamReader, c elliptic.Curve, hash []byte) (r, s *big.Int, err error) {
	N := c.Params().N
	if N.Sign() == 0 {
		return nil, nil, errZeroParam
	}
	var k, kInv *big.Int
	for {
		for {
			k, err = randFieldElement(c, *csprng)
			if err != nil {
				r = nil
				return
			}

			if in, ok := priv.Curve.(invertible); ok {
				kInv = in.Inverse(k)
			} else {
				kInv = fermatInverse(k, N) // N！=0
			}

			r, _ = priv.Curve.ScalarBaseMult(k.Bytes())
			r.Mod(r, N)
			if r.Sign() != 0 {
				break
			}
		}

		e := hashToInt(hash, c)
		s = new(big.Int).Mul(priv.D, r)
		s.Add(s, e)
		s.Mul(s, kInv)
		s.Mod(s, N) // N！=0
		if s.Sign() != 0 {
			break
		}
	}

	return
}

// SignASN1对散列进行签名（散列应该是对较大消息进行散列的结果）
// 使用私钥priv。如果散列长度大于
// 私钥的曲线顺序，哈希将被截断为该长度。信息技术
// 返回ASN.1编码的签名。私钥的安全性
// 取决于兰德的熵。
func SignASN1(rand io.Reader, priv *PrivateKey, hash []byte) ([]byte, error) {
	return priv.Sign(rand, hash, nil)
}

// 验证使用公钥pub在散列的r，s中验证签名。它的
// 返回值记录签名是否有效。
func Verify(pub *PublicKey, hash []byte, r, s *big.Int) bool {
	// 见[NSA]3.4.2
	c := pub.Curve
	N := c.Params().N

	if r.Sign() <= 0 || s.Sign() <= 0 {
		return false
	}
	if r.Cmp(N) >= 0 || s.Cmp(N) >= 0 {
		return false
	}
	return verify(pub, c, hash, r, s)
}

func verifyGeneric(pub *PublicKey, c elliptic.Curve, hash []byte, r, s *big.Int) bool {
	e := hashToInt(hash, c)
	var w *big.Int
	N := c.Params().N
	if in, ok := c.(invertible); ok {
		w = in.Inverse(s)
	} else {
		w = new(big.Int).ModInverse(s, N)
	}

	u1 := e.Mul(e, w)
	u1.Mod(u1, N)
	u2 := w.Mul(r, w)
	u2.Mod(u2, N)

	// 检查机具是否为S1*g+S2*p
	var x, y *big.Int
	if opt, ok := c.(combinedMult); ok {
		x, y = opt.CombinedMult(pub.X, pub.Y, u1.Bytes(), u2.Bytes())
	} else {
		x1, y1 := c.ScalarBaseMult(u1.Bytes())
		x2, y2 := c.ScalarMult(pub.X, pub.Y, u2.Bytes())
		x, y = c.Add(x1, y1, x2, y2)
	}

	if x.Sign() == 0 && y.Sign() == 0 {
		return false
	}
	x.Mod(x, N)
	return x.Cmp(r) == 0
}

// VerifyASN1使用
// 公开密钥，发布。其返回值记录签名是否有效。
func VerifyASN1(pub *PublicKey, hash, sig []byte) bool {
	var (
		r, s  = &big.Int{}, &big.Int{}
		inner cryptobyte.String
	)
	input := cryptobyte.String(sig)
	if !input.ReadASN1(&inner, asn1.SEQUENCE) ||
		!input.Empty() ||
		!inner.ReadASN1Integer(r) ||
		!inner.ReadASN1Integer(s) ||
		!inner.Empty() {
		return false
	}
	return Verify(pub, hash, r, s)
}

type zr struct {
	io.Reader
}

// Read将dst的内容替换为零。
func (z *zr) Read(dst []byte) (n int, err error) {
	for i := range dst {
		dst[i] = 0
	}
	return len(dst), nil
}

var zeroReader = &zr{}
